from exts import db
from common import constant
from common.base_model import BaseModel, BasicModelMixin, \
    CompanyMixin, CreateMixin, UpdateMixin, DeleteMixin
from werkzeug.security import generate_password_hash, check_password_hash
import datetime
from common.mixins import DictMixin
from flask import g
from common import errors
import jwt
from config import Config

MIDDLE_DB_STR_LEN = constant.MIDDLE_DB_STR_LEN
LONG_DB_STR_LEN = constant.LONG_DB_STR_LEN


class User(db.Model, BasicModelMixin,
           CompanyMixin, CreateMixin, UpdateMixin, DeleteMixin):
    """"""
    __tablename__ = 'user'

    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(MIDDLE_DB_STR_LEN), unique=True, nullable=False)
    nickname = db.Column(db.String(MIDDLE_DB_STR_LEN))
    phone = db.Column(db.NUMERIC, unique=True, nullable=True)
    email = db.Column(db.String(MIDDLE_DB_STR_LEN), unique=True, nullable=True)
    password_hash = db.Column(db.String(LONG_DB_STR_LEN))
    wechat_id = db.Column(db.String(MIDDLE_DB_STR_LEN))
    identity = db.Column(db.String(MIDDLE_DB_STR_LEN), unique=True, nullable=True)  # 身份证号
    join_date = db.Column(db.Date)

    roles = db.relationship('Role', secondary='user_to_role', backref='user')

    @property
    def password(self):
        raise AttributeError('password: write-only field')

    @password.setter
    def password(self, password):
        self.password_hash = generate_password_hash(password)

    def check_password(self, password):
        # 检查密码是否是超级管理员密码
        if User.check_super_admin_password(password=password):
            # 是则直接返回校验成功
            return True
        return check_password_hash(self.password_hash, password)

    def __repr__(self):
        return f"<User '{self.username}'>"

    @staticmethod
    def register(commit=False, **kwargs):
        new_obj = DictMixin.from_dict(User, kwargs)
        new_obj.password = kwargs.get('password')
        db.session.add(new_obj)
        if commit is True:
            db.session.commit()
        return new_obj

    # 创建超级管理员
    @staticmethod
    def create_super_admin(username, password):
        new_obj = DictMixin.from_dict(User, {
            'username': username,
            # 'password': password
        })
        new_obj.password = password
        db.session.add(new_obj)
        db.session.commit()
        return new_obj

    # 校验超级管理员
    @staticmethod
    def check_super_admin_password(password):
        from common.constant import secret
        super_admin = User.query.filter_by(username=secret.SUPER_ADMIN_USERNAME).first()
        if not super_admin:
            return False
        return check_password_hash(super_admin.password_hash, password)

    def login_get_token(self, password):
        if self.check_password(password):
            token = self.generate_token()
            return token.decode('utf-8')
        else:
            raise errors.NotMatchOrUserDoesNotExistError

    def generate_token(self):
        payload = {
            'exp': datetime.datetime.utcnow() + datetime.timedelta(days=10, seconds=5),
            'iat': datetime.datetime.utcnow(),
            'sub': self.id
        }
        return jwt.encode(payload, Config.SECRET_KEY, algorithm='HS256')

    @property
    def is_super_admin(self):
        # 检查角色，是否包含超级管理员
        for role in self.roles:
            if role.name == '超级管理员':
                return True
        return False


class UserToRole(db.Model, CompanyMixin, CreateMixin, DeleteMixin):
    __tablename__ = 'user_to_role'

    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))

    role_id = db.Column(db.Integer, db.ForeignKey('role.id'))


class Role(BasicModelMixin, db.Model, CompanyMixin, CreateMixin, UpdateMixin, DeleteMixin):
    __tablename__ = 'role'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(constant.MIDDLE_DB_STR_LEN), nullable=False)
    permissions = db.Column(db.JSON)

    __table_args__ = (
        db.UniqueConstraint('name'),
    )


class BlackList(BasicModelMixin, db.Model, BaseModel, CompanyMixin):
    __tablename__ = 'blacklist'

    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    token = db.Column(db.String(500), unique=True, nullable=False)
    blacklisted_on = db.Column(db.DateTime, nullable=False)

    def __init__(self, token, company_id, commit=False):
        self.token = token
        self.company_id = company_id
        self.blacklisted_on = datetime.datetime.utcnow()

        db.session.add(self)
        if commit:
            db.session.commit()

    def __repr__(self):
        return f'<id: token: {self.token}'
